using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using CommunityServer.Components;
using CommunityServer.Blogs.Components;
using CommunityServer.Spam;

using CS.Modules;
using CS.Spam;

namespace CS
{
    public class PostTweeter
    {
        #region Constants
        readonly string UPDATE_URL = "http://twitter.com/statuses/update.json?status={0}&source=CommunityServer";
        readonly int MAX_MESSAGE_LENGTH = 140;
        readonly string TINY_URL = "http://tinyurl.com/api-create.php?url={0}";
        #endregion

        #region Singleton
        private static PostTweeter _instance;
        /// <summary>
        /// accessor to single instance of PostTweeter
        /// </summary>
        public static PostTweeter Instance
        {
            get
            {
                lock (typeof(PostTweeter))
                {
                    if (_instance == null)
                    {
                        _instance = new PostTweeter();
                    }
                }

                return _instance;
            }
        }

        /// <summary>
        /// protected constructor
        /// </summary>
        protected PostTweeter()
        {
        }

        #endregion

        /// <summary>
        /// Initializes the pseudo spam rule that is used for settings
        /// and initializes the CS module used to auto-tweet posts
        /// </summary>
        /// <remarks>Only rules inside this assembly should be allowed to call this</remarks>
        /// <param name="rule">The rule to use for settings</param>
        internal void Init(TweetModuleRule rule)
        {
            _rule = rule;
            
            CSApplication app = CSCache.Get("CSApplication") as CSApplication;
            if (app == null)
            {
                EventLogs.Warn("Could not get application from cache during module initialization.", "Twitter", 987);
            }
            else
            {
                ICSModule module = new TweetModule();
                module.Init(app, null);
            }
        }

        #region Properties

        private TweetModuleRule _rule;
        public TweetModuleRule Rule
        {
            get { return _rule; }
        }

        #endregion


        #region Helper functions

        /// <summary>
        /// Sends a tweet to the account configured in the Pseudo Tweet "Spam" Rule based
        /// on the post information
        /// </summary>
        /// <param name="post">the post to tweet</param>
        /// <returns>success indication</returns>
        public bool TweetPost(WeblogPost post)
        {
            bool result = false;

            string url = string.Format("http://{0}{1}", CSContext.Current.CurrentUri.Host, post.ViewPostURL);
            string title = post.Subject;

            // convert to tiny url
            string tinyUrl = MakeTiny(url);

            // trim to title to specified characters
            int maxTitleLength = MAX_MESSAGE_LENGTH - (Rule.TweetMessage.Length + tinyUrl.Length) - 9;
            if (title.Length > maxTitleLength)
            {
                title = title.Substring(0, maxTitleLength);
            }

            // add as much of the content as possible
            string excerpt = post.ForceExcerpt;
            int maxExcerptLength = MAX_MESSAGE_LENGTH - (Rule.TweetMessage.Length + tinyUrl.Length + title.Length) - 9;
            if (excerpt.Length > maxExcerptLength)
            {
                excerpt = excerpt.Substring(0, maxExcerptLength);
            }

            string message = string.Format(Rule.TweetMessage, title, excerpt, tinyUrl);

            string jsonStatus = UpdateStatus(message);
            if (!string.IsNullOrEmpty(jsonStatus)) { result = true; }

            return result;
        }

        /// <summary>
        /// Attempts to make a tiny URL out of a big URL
        /// </summary>
        /// <param name="bigUrl">the url to make tiny</param>
        /// <returns>the tiny URL</returns>
        public string MakeTiny(string bigUrl)
        {
            string rtn = string.Empty;
            try
            {
                // does the api-create.php not take an encoded url?? evidently not...
                rtn = Request(string.Format(TINY_URL, bigUrl));
            }
            catch (Exception ex)
            {
                // log tiny url exception
                EventLogs.Warn(string.Format("An exception occured in the Twitter Module during TinyURL creation: {0}.", ex.Message), "Twitter", 981);
                return rtn;
            }
            return rtn;
        }

        /// <summary>
        /// Updates the status for the user configured via the Pseudo Tweet "Spam" Rule
        /// </summary>
        /// <param name="text">The text for the status update</param>
        /// <returns>the JSON representation of the user's current status after the update</returns>
        public string UpdateStatus(string text)
        {
            string rtn = string.Empty;
            try
            {
                rtn = Request(string.Format(UPDATE_URL, Globals.UrlEncode(text)), new NetworkCredential(Rule.Username, Rule.Password), "POST");
                if (Rule.Broadcast)
                {
                    string moduleStatus = Request(string.Format(UPDATE_URL, Globals.UrlEncode(text)), new NetworkCredential(Rule.ModuleUser, Rule.ModulePassword), "POST");
                }
            }
            catch (Exception ex)
            {
                // log tiny url exception
                EventLogs.Warn("An exception occured in the Twitter Module during a Tweet attempt.", "Twitter", 982);
                return rtn;
            }
            return rtn;
        }

        #region Request methods
        private string Request(string url)
        {
            return Request(url, null, "GET", string.Empty, string.Empty);
        }
        private string Request(string url, NetworkCredential credentials)
        {
            return Request(url, credentials, "GET", string.Empty, string.Empty);
        }
        private string Request(string url, NetworkCredential credentials, string method)
        {
            return Request(url, credentials, method, string.Empty, string.Empty);
        }
        private string Request(string url, NetworkCredential credentials, string method, string content)
        {
            return Request(url, credentials, method, content, string.Empty);
        }
        private string Request(string url, NetworkCredential credentials, string method, string content, string contentType)
        {
            string rtn = string.Empty;
            Stream stream = null;
            try
            {
                // create request
                HttpWebRequest request = CSRequest.CreateRequest(url);
                if (credentials != null) { request.Credentials = credentials; }
                if (!string.IsNullOrEmpty(method)) { request.Method = method; }
                if (!string.IsNullOrEmpty(contentType)) { request.ContentType = contentType; }
                request.ContentLength = content.Length;
                request.Headers.Add("X-Twitter-Client:CommunityServer");
                request.Headers.Add("X-Twitter-Client-Version:v1.0a");
                //request.Headers.Add("X-Twitter-Client-URL:http://communityserver.org/twitter_client.xml");

                // write content if we have any
                if (!string.IsNullOrEmpty(content))
                {
                    using (Stream requestStream = request.GetRequestStream())
                    {
                        requestStream.Write(Encoding.UTF8.GetBytes(content), 0, content.Length);
                    }
                }
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                DateTime lastModified = response.LastModified;
                Encoding enc = Encoding.UTF8;
                try
                {
                    if (!string.IsNullOrEmpty(response.ContentEncoding))
                    {
                        enc = Encoding.GetEncoding(response.ContentEncoding);
                    }
                }
                catch { }
                stream = response.GetResponseStream();
            }
            catch (HttpListenerException ex)
            {
                switch (ex.ErrorCode)
                {
                    case 401:
                        // handle invalid credentials
                        EventLogs.Warn("Invalid credentials used for Twitter.", "Twitter", 983);
                        break;
                }
                return rtn;
            }

            if (stream != null)
            {
                using (TextReader reader = new StreamReader(stream))
                {
                    rtn = reader.ReadToEnd();
                }
                stream.Close();
            }

            return rtn;
        }
        #endregion

        #endregion
    }
}
